package svc

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/zeromicro/go-zero/core/stores/redis"
	"github.com/zeromicro/go-zero/rest"
	"private-domain-overseas-service/admin/business/api/internal/config"
	"private-domain-overseas-service/admin/business/api/internal/middleware"
	"private-domain-overseas-service/pkg/sms"
	"private-domain-overseas-service/tools/ent"
	"private-domain-overseas-service/tools/ent/migrate"

	"entgo.io/ent/dialect"
	_ "github.com/go-sql-driver/mysql"
	"github.com/zeromicro/go-zero/core/logx"
)

type ServiceContext struct {
	Config                       config.Config
	Orm                          *ent.Client
	OrmDb                        *sql.DB
	SmsService                   *sms.SmsService
	BizRedis                     *redis.Redis
	CheckAndAuthButtonMiddleware rest.Middleware
	CheckAndFlushTokenMiddleware rest.Middleware
	CheckAndRecordLogMiddleware  rest.Middleware
}

func NewServiceContext(c config.Config) *ServiceContext {
	orm := initOrm(c)
	db, _ := initDB(c)

	redisStore := redis.New(c.BizRedis.Host, redis.WithPass(c.BizRedis.Pass))

	smsClient, err := sms.NewSmsService(c.Sms.SecretId, c.Sms.SecretKey, c.Sms.SDKAppID, c.Sms.LoginTemplateID, c.Sms.ResetTemplateID)
	if err != nil {
		logx.Errorf("NewSmsService error: %v", err)
		return nil
	}

	return &ServiceContext{
		Config:                       c,
		Orm:                          orm,
		OrmDb:                        db,
		SmsService:                   smsClient,
		BizRedis:                     redisStore,
		CheckAndFlushTokenMiddleware: middleware.NewCheckAndFlushTokenMiddleware(c, c.Auth.AccessSecret).Handle,
		CheckAndAuthButtonMiddleware: middleware.NewCheckAndAuthButtonMiddleware(orm).Handle,
		CheckAndRecordLogMiddleware:  middleware.NewCheckAndRecordLogMiddleware(orm).Handle,
	}
}

// initOrm
func initOrm(c config.Config) *ent.Client {
	ops := make([]ent.Option, 0)
	if c.ShowSQL {
		ops = append(ops, ent.Debug())
	}
	client, err := ent.Open(dialect.MySQL, c.DataSource, ops...)
	if err != nil {
		logx.Errorf("ent.Open error: %v", err)
		panic(err)
	}
	if err := client.Schema.Create(context.Background(),
		migrate.WithDropColumn(false), // 删除多余的列
		migrate.WithDropIndex(false),  // 删除多余的索引
	); err != nil {
		logx.Errorf("ent.Schema.Create error: %v", err)
		panic(err)
	}

	return client
}

// initDB initializes the database connection
func initDB(c config.Config) (*sql.DB, error) {
	db, err := sql.Open("mysql", c.DataSource)
	if err != nil {
		return nil, fmt.Errorf("failed to open database: %w", err)
	}

	if err := db.Ping(); err != nil {
		return nil, fmt.Errorf("failed to ping database: %w", err)
	}

	// Optionally set connection pool settings if needed
	db.SetMaxOpenConns(25)   // 设置最大打开连接数
	db.SetMaxIdleConns(25)   // 设置最大闲置连接数
	db.SetConnMaxLifetime(0) // 设置连接的最大生命周期

	return db, nil
}

// Close closes the database connection and other resources
func (sc *ServiceContext) Close() {
	if sc.OrmDb != nil {
		if err := sc.OrmDb.Close(); err != nil {
			logx.Errorf("failed to close database: %v", err)
		}
	}
	if sc.Orm != nil {
		if err := sc.Orm.Close(); err != nil {
			logx.Errorf("failed to close ORM: %v", err)
		}
	}
}
